#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>  //用于设备域名 MDNS.begin("esp32")
#include <EEPROM.h>
#include "WifiConfig.h"
#include "WebInterface.h"

String scanNetworksID = "";      //用于储存扫描到的WiFi ID
IPAddress apIP(192, 168, 4, 1);  //设置AP的IP地址
String wifi_ssid = "";           //暂时存储wifi账号密码
String wifi_pass = "";           //暂时存储wifi账号密码

S_ConfClock s_conf_clock = {
  e_ChimeHour_Off, 7, 0, e_ClockType_24, true, e_SecondScreen_Temp, e_usb_position_left
};


//定义根目录首页网页HTML源代码
#define TAIL_HTML "</ul><script> document.addEventListener('DOMContentLoaded', function() {var listItems = document.querySelectorAll('#networkList li'); listItems.forEach(function(item) {item.addEventListener('click', function() {var input = document.getElementById('ssid'); input.value = this.textContent || this.innerText; }); }); }); </script> </body></html>"
#define ROOT_HTML "<!DOCTYPE html><html><head><title>WIFI Config</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" \
                  "</head><style type=\"text/css\">.input{display: block; margin-top: 10px;}.input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}.input input{height: 30px;width: 200px;}.btn{width: 120px; height: 35px; background-color: #4CAF50; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}.btn:active {background-color: #3e8e41;}</style>" \
                  "<body><form method=\"POST\" action=\"configwifi\"><label class=\"input\"><span>WiFi SSID</span><input type=\"text\" id=\"ssid\" name=\"ssid\" value=\"\"></label><label class=\"input\"><span>WiFi PASS</span><input type=\"text\" id=\"pass\" name=\"pass\"></label><input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> " \
                  "<p><span> Nearby wifi:</p></form> <ul id=\"networkList\">"

#define SET_BACK_HTML "\
<!DOCTYPE html>\
<html lang='ch'>\
<head>\
<meta charset='UTF-8'>\
<title>联网时钟配置界面</title>\
</head>\
<body bgcolor='#f4f4f4' style='text-align:center'>\
<h1>设置成功</h1>\
<input type='button' value='返回' onclick='history.back()' style='width: 100px; height: 35px;'>\
</body>\
</html>"


DNSServer dnsServer;                //创建dnsServer实例
ESP8266WebServer server(PORT_WEB);  //开启web服务, 创建TCP SERVER,参数: 端口号,最大连接数

//初始化AP模式
void initSoftAP() {
  WiFi.mode(WIFI_AP);                                          //配置为AP模式
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));  //设置AP热点IP和子网掩码
  if (WiFi.softAP(AP_SSID)) {                                  //开启AP热点,如需要密码则添加第二个参数
    //打印相关信息
    Serial.println("ESP8266 SoftAP is right.");
    Serial.print("Soft-AP IP address = ");
    Serial.println(WiFi.softAPIP());
    Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
  } else {  //开启热点失败
    Serial.println("WiFiAP Failed, restart now...");
    ESP.restart();  //重启复位esp32
  }
}

//初始化DNS服务器
void initDNS() {
  //判断将所有地址映射到esp8266的ip上是否成功
  if (dnsServer.start(PORT_DNS, "*", apIP)) {
    Serial.println("start dnsserver success.");
  } else {
    Serial.println("start dnsserver failed.");
  }
}

//扫描WiFi
bool scanWiFi() {
  scanNetworksID.clear();
  Serial.println("scan start");
  // 扫描附近WiFi
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0) {
    Serial.println("no networks found");
    scanNetworksID = "no WIFI found";
    return false;
  } else {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; ++i) {
      // Print SSID and RSSI for each network found
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == AUTH_OPEN) ? " " : "*");
      scanNetworksID += "<li>" + WiFi.SSID(i) + "</li>";
      delay(10);
    }
    return true;
  }
}

void handleConfigWifi(void) {
  //返回http状态
  if (server.hasArg("ssid")) {  //判断是否有账号参数
    Serial.print("got ssid:");
    wifi_ssid = server.arg("ssid");  //获取html表单输入框name名为"ssid"的内容
    // strcpy(sta_ssid, server.arg("ssid").c_str());//将账号参数拷贝到sta_ssid中
    Serial.println(wifi_ssid);
  } else {  //没有参数
    Serial.println("error, not found ssid");
    server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid");  //返回错误页面
    return;
  }
  //密码与账号同理
  if (server.hasArg("pass")) {
    Serial.print("got password:");
    wifi_pass = server.arg("pass");  //获取html表单输入框name名为"pwd"的内容
    //strcpy(sta_pass, server.arg("pass").c_str());
    Serial.println(wifi_pass);
  } else {
    Serial.println("error, not found password");
    server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
    return;
  }

  server.send(200, "text/html", "<meta charset='UTF-8'><span style='font-size:35px;'>SSID:" + wifi_ssid + "<br />password:" + wifi_pass + "<br />已取得WiFi信息,正在尝试连接,请手动关闭此页面。</span>");  //返回保存成功页面
  delay(2000);
  WiFi.softAPdisconnect(true);  //参数设置为true，设备将直接关闭接入点模式，即关闭设备所建立的WiFi网络。
  server.close();               //关闭web服务

  WiFi.softAPdisconnect();  //在不输入参数的情况下调用该函数,将关闭接入点模式,并将当前配置的AP热点网络名和密码设置为空值.
  Serial.println("WiFi Connect SSID:" + wifi_ssid + "  PASS:" + wifi_pass);
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("开始调用连接函数connectToWiFi()..");
    connectToWiFi(CONN_TIMEOUT);
  } else {
    Serial.println("提交的配置信息自动连接成功..");
  }
}

void handleWorkRoot(void) {
  char *html_buf = (char *)malloc(4096+256);
  server.send(200, "text/html", WebInterface_WorkConfig_html(html_buf, &s_conf_clock));
  free(html_buf);
}

//处理网站根目录“/”(首页)的访问请求,将显示配置wifi的HTML页面
void handleRoot() {
  if (WiFi.getMode() & WIFI_AP) {
    server.send(200, "text/html", ROOT_HTML + scanNetworksID + TAIL_HTML);
  } else {
    handleWorkRoot();
  }
}

// 处理配置请求
void handleWorkConfig(void) {
  if (server.hasArg("talktime")) {             //判断是否有账号参数
    String talktime = server.arg("talktime");  //获取html表单输入框name名为"ssid"的内容
    Serial.print("talktime:");
    Serial.println(talktime.toInt());
    s_conf_clock.chimehour = (E_ChimeHour)talktime.toInt();
    EEPROM.write(1, (uint8_t)s_conf_clock.chimehour);
  }

  if (server.hasArg("brightnessControl")) {
    String brightnessControl = server.arg("brightnessControl");
    Serial.print("brightnessControl:");
    Serial.println(brightnessControl.toInt());
    s_conf_clock.brightness = brightnessControl.toInt();
    EEPROM.write(2, (uint8_t)s_conf_clock.brightness);
  }

  if (server.hasArg("auto_brightness")) {
    s_conf_clock.auto_brightness = true;
    EEPROM.write(3, (uint8_t)s_conf_clock.auto_brightness);
  } else {
    s_conf_clock.auto_brightness = false;
    EEPROM.write(3, (uint8_t)s_conf_clock.auto_brightness);
  }

  if (server.hasArg("gender")) {
    String gender = server.arg("gender");
    Serial.print("gender:");
    Serial.println(gender);
    s_conf_clock.screen = (E_SecondScreen)gender.toInt();
    EEPROM.write(4, (uint8_t)s_conf_clock.screen);
  }

  if (server.hasArg("usb_position")) {
    String usb_position = server.arg("usb_position");
    Serial.print("usb_position:");
    Serial.println(usb_position);
    s_conf_clock.usb_position = (E_UsbPosition)usb_position.toInt();
    EEPROM.write(5, (uint8_t)s_conf_clock.usb_position);
  }

  if (server.hasArg("date_interval")) {
    String date_interval = server.arg("date_interval");
    Serial.print("date_interval:");
    Serial.println(date_interval);
    s_conf_clock.date_interval = date_interval.toInt();
    EEPROM.write(6, (uint8_t)s_conf_clock.date_interval);
  }

  if (server.hasArg("clock_type")) {
    String clock_type = server.arg("clock_type");
    Serial.print("clock_type:");
    Serial.println(clock_type);
    s_conf_clock.clock_type = clock_type.toInt();
    EEPROM.write(7, (uint8_t)s_conf_clock.clock_type);
  }

  // 保存到EEPROM
  EEPROM.commit();

  server.send(200, "text/html", SET_BACK_HTML);  //返回保存成功页面
}

void InitSysConfig(void) {
  if (EEPROM.read(0) == 0x5a && EEPROM.read(3) < 2) { /* 时钟已经配置过 */
    s_conf_clock.chimehour = (E_ChimeHour)EEPROM.read(1);
    s_conf_clock.brightness = EEPROM.read(2);
    s_conf_clock.auto_brightness = (bool)EEPROM.read(3);
    s_conf_clock.screen = (E_SecondScreen)EEPROM.read(4);
    s_conf_clock.usb_position = (E_UsbPosition)EEPROM.read(5);
    s_conf_clock.date_interval = EEPROM.read(6);
    s_conf_clock.clock_type = EEPROM.read(7);
  } else { /* 时钟初始状态，储存初始数据 */
    EEPROM.write(0, 0x5a);
    EEPROM.write(1, (uint8_t)s_conf_clock.chimehour);
    EEPROM.write(2, (uint8_t)s_conf_clock.brightness);
    EEPROM.write(3, (uint8_t)s_conf_clock.auto_brightness);
    EEPROM.write(4, (uint8_t)s_conf_clock.screen);
    EEPROM.write(5, (uint8_t)s_conf_clock.usb_position);
    EEPROM.write(6, (uint8_t)s_conf_clock.date_interval);
    EEPROM.write(7, (uint8_t)s_conf_clock.clock_type);
    EEPROM.commit();
  }
}

//初始化WebServer
void initWebServer(void) {
  //给设备设定域名esp32,完整的域名是esp8266.local ??
  if (MDNS.begin("WifiClock")) {
    Serial.println("MDNS responder started");
  }

  //3  当浏览器请求服务器根目录(网站首页)时调用自定义函数handleRoot处理，设置主页回调函数，必须添加第二个参数HTTP_GET，否则无法强制门户
  server.on("/", HTTP_GET, handleRoot);

  //3  当浏览器请求服务器/configwifi(表单字段)目录时调用自定义函数handleConfigWifi处理
  server.on("/configwifi", HTTP_POST, handleConfigWifi);
  server.onNotFound(handleRoot);  //当浏览器请求的网络资源无法在服务器找到时调用自定义函数handleNotFound处理
  server.begin();                 //启动TCP SERVER
  Serial.println("WebServer started!");
}

//工作配置
void initWorkConfWebServer(void) {
  if (MDNS.begin("WifiClock")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", HTTP_GET, handleRoot);
  server.on("/workconfig", HTTP_POST, handleWorkConfig);
  server.onNotFound(handleRoot);

  server.begin();  //启动TCP SERVER

  Serial.println("Work Config WebServer started!");
}

//用于配置WiFi
void wifiConfig() {
  scanWiFi();
  initSoftAP();
  initDNS();
  initWebServer();
}

void connectToWiFi(int timeOut_s) {
  //设置为STA模式并连接WIFI
  WiFi.mode(WIFI_STA);
  //用字符串成员函数c_str()生成一个const char*指针，指向以空字符终止的数组,即获取该字符串的指针。
  if (wifi_ssid != "") {
    Serial.println("用web配置信息连接.");

    WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
    wifi_ssid = "";
    wifi_pass = "";
  } else {
    Serial.println("用nvs保存的信息连接.");
    WiFi.begin();  //连接上一次连接成功的wifi
  }
  //WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
  int Connect_time = 0;                    //用于连接计时，如果长时间连接不成功，复位设备
  while (WiFi.status() != WL_CONNECTED) {  //等待WIFI连接成功
    Serial.print(".");
    digitalWrite(PIN_LED, !digitalRead(PIN_LED));
    delay(500);
    Connect_time++;
    if (Connect_time > 2 * timeOut_s) {  //长时间连接不上，重新进入配网页面
      digitalWrite(PIN_LED, LOW);
      Serial.println("connect WIFI fail...");
      return;  //跳出 防止无限初始化
    }
  }
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("WIFI connect Success");
    Serial.printf("SSID:%s", WiFi.SSID().c_str());
    Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
    Serial.print("LocalIP:");
    Serial.print(WiFi.localIP());
    Serial.print(" ,GateIP:");
    Serial.println(WiFi.gatewayIP());
    Serial.print("WIFI status is:");
    Serial.print(WiFi.status());
    digitalWrite(PIN_LED, HIGH);
    server.stop();

    initWorkConfWebServer();
  }
}

//LED闪烁,led为脚号,n为次数,t为时间间隔ms
void blinkLED(int led, int n, int t) {
  for (int i = 0; i < 2 * n; i++) {
    digitalWrite(led, !digitalRead(led));
    delay(t);
  }
}

void restoreWiFi(void) {
  dnsServer.stop();
  server.stop();
  MDNS.close();
  Serial.println("准备重启设备..");
  blinkLED(PIN_LED, 25, 100);  //LED闪烁5次
  digitalWrite(PIN_LED, LOW);
  wifiConfig();
}


int Connect_time = 0;
void reconnectToWiFi(void) {

  if (Connect_time > 30) {
    WiFi.mode(WIFI_STA);
    Serial.println("用nvs保存的信息连接.");
    WiFi.begin();  //连接上一次连接成功的wifi
    Connect_time = 0;
  }

  digitalWrite(PIN_LED, !digitalRead(PIN_LED));
  delay(500);
  Connect_time++;
}

bool checkWifiConnect(void) {
  if (WiFi.getMode() != WIFI_STA) {  //快闪：配置状态
    digitalWrite(PIN_LED, !digitalRead(PIN_LED));
    delay(100);
  }

  if (WiFi.status() != WL_CONNECTED) {
    if (WiFi.getMode() != WIFI_AP && WiFi.getMode() != WIFI_AP_STA) {
      reconnectToWiFi();
    }
    return false;
  }

  if (WiFi.status() == WL_CONNECTED && Connect_time > 0) {
    digitalWrite(PIN_LED, LOW);  // 亮灯，表示wifi恢复，网络是否通畅待测
    Connect_time = 0;
    return true;  // wifi连接刚恢复
  }

  return false;
}

void checkRequest(void) {
  dnsServer.processNextRequest();  //检查客户端DNS请求
  server.handleClient();           //检查客户端(浏览器)http请求
}
